using System;
using System.Collections.Generic;
using System.Text;

namespace Weazel.Microblaze.Simulator.Engine
{
  public class MemoryException
    : Exception
  {
      private Dictionary<Component, string> componentMessages;

    public enum ErrorType
    {
      ReadError,
      WriteError,
      WriteErrorRom,
      AddressOverlap,
      LoadError
    }

    private ErrorType type;
    public ErrorType Type
    {
      get { return type; }
    }

    private uint address;
    public uint Address
    {
      get { return address; }
    }

      private void getComponentMessages(SimulationContext simulator)
      {
          foreach (Component component in simulator.Components)
          {
              componentMessages.Add(component, component.GetStatusString());
          }
      }

    public MemoryException(SimulationContext simulator, ErrorType type)
    {
        if (simulator == null)
        {
            throw new NullReferenceException();
        }

      this.type = type;
      this.componentMessages = new Dictionary<Component, string>();
      getComponentMessages(simulator);
    }

    public MemoryException(SimulationContext simulator, ErrorType type, uint address) 
    {
        if (simulator == null)
        {
            throw new NullReferenceException();
        }

      this.type = type;
      this.address = address;
      this.componentMessages = new Dictionary<Component, string>();
      getComponentMessages(simulator);
    }

    public override string Message
    {        
      get
      {
          StringBuilder sb = new StringBuilder();

          sb.Append("Memory error: ");

          switch (type)
          {
              case ErrorType.AddressOverlap:
                  sb.AppendLine(string.Format("Address overlap during initialization"));
                  break;

              case ErrorType.LoadError:
                  sb.AppendLine("load error (unspecified)");
                  break;

              case ErrorType.ReadError:
                  sb.AppendLine(string.Format("Error reading address 0x{0:X}", address));
                  break;

              case ErrorType.WriteError:
                  sb.AppendLine(string.Format("Error writing address 0x{0:X}", address));
                  break;

              case ErrorType.WriteErrorRom:
                  sb.AppendLine(string.Format("Attempted to write ROM address 0x{0:X}", address));
                  break;
              
              default:
                  sb.AppendLine("unknown error");
                  break;
          }

          sb.AppendLine();
          foreach (Component component in componentMessages.Keys)
          {
              sb.AppendLine(component.GetType().ToString() + ":");
              sb.AppendLine(componentMessages[component]);
          }

          return sb.ToString();
      }
    }
  }
}
